前端analysis | 知其所以然

Flutter Windows tab 自动更新

2025-07-23

Flutter 的 Tab 是完全可以通过配置动态更新的。可以做到:

  • ✅ Tab 的数量从 3 → 4 → 5 动态变化(甚至运行时改变)
  • ✅ 每个 Tab 的标题、图标、URL 都由配置文件(如 JSON)控制
  • ✅ 配置可以从本地或远程加载,实现远程动态更新界面结构

✅ 实现原理概述

  1. 定义一个 Tab 配置结构,比如本地或远程的 JSON 文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
{
"title": "首页",
"icon": "home",
"url": "https://yourdomain.com/tab1/index.html"
},
{
"title": "发现",
"icon": "search",
"url": "https://yourdomain.com/tab2/index.html"
},
{
"title": "我",
"icon": "person",
"url": "https://yourdomain.com/tab3/index.html"
}
]

可以在某次更新后,变成 5 个:

1
2
3
4
5
6
7
8
9
10
11
12
13
[
...
{
"title": "设置",
"icon": "settings",
"url": "https://yourdomain.com/tab4/index.html"
},
{
"title": "帮助",
"icon": "help",
"url": "https://yourdomain.com/tab5/index.html"
}
]

✅ Flutter 动态加载配置 + 构建 Tab 示例(核心代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;

void main() {
runApp(MyApp());
}

class TabItem {
final String title;
final IconData icon;
final String url;

TabItem({required this.title, required this.icon, required this.url});

factory TabItem.fromJson(Map<String, dynamic> json) {
return TabItem(
title: json['title'],
icon: _iconFromName(json['icon']),
url: json['url'],
);
}

static IconData _iconFromName(String name) {
switch (name) {
case 'home': return Icons.home;
case 'search': return Icons.search;
case 'person': return Icons.person;
case 'settings': return Icons.settings;
case 'help': return Icons.help;
default: return Icons.device_unknown;
}
}
}

class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
List<TabItem> tabs = [];
int _selectedIndex = 0;

@override
void initState() {
super.initState();
loadTabs();
}

Future<void> loadTabs() async {
final String jsonStr = await rootBundle.loadString('assets/tab_config.json');
final List<dynamic> jsonList = json.decode(jsonStr);
setState(() {
tabs = jsonList.map((e) => TabItem.fromJson(e)).toList();
});
}

@override
Widget build(BuildContext context) {
if (tabs.isEmpty) {
return MaterialApp(
home: Scaffold(body: Center(child: CircularProgressIndicator())),
);
}

return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text(tabs[_selectedIndex].title)),
body: WebView(
initialUrl: tabs[_selectedIndex].url,
javascriptMode: JavascriptMode.unrestricted,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
items: tabs
.map((tab) =>
BottomNavigationBarItem(icon: Icon(tab.icon), label: tab.title))
.toList(),
onTap: (index) {
setState(() {
_selectedIndex = index;
});
},
),
),
);
}
}

✅ JSON 配置说明

  • 放置路径:assets/tab_config.json
  • 记得在 pubspec.yaml 中声明资源:
1
2
3
flutter:
assets:
- assets/tab_config.json

✅ 实现“远程配置”的动态更新?

可以把 tab_config.json 放到服务器上,通过 HTTP 获取:

1
2
final response = await http.get(Uri.parse('https://yourdomain.com/tab_config.json'));
final List<dynamic> jsonList = json.decode(response.body);

这样就可以远程动态控制:
✅ Tab 数量
✅ Tab 标题 / 图标 / 跳转页面
✅ 无需发版即可更新用户体验


✅ 总结

目标 是否可行 实现方式
动态控制 Tab 数量(3 → 5) 配置驱动 + 动态构建
配置从本地加载 assets 中 JSON
配置从远程加载 HTTP + JSON
更新不发版 远程配置 + 缓存机制

🧱 目录结构建议(Flutter 项目)

1
2
3
4
5
6
7
8
flutter_vue_shell/
├── assets/
│ ├── tab1/index.html
│ ├── tab2/index.html
│ └── tab3/index.html
├── lib/
│ └── main.dart
├── pubspec.yaml

✨ 第一步:准备 Vue3 页面

使用 Vue3(比如 Vite)构建的三个页面:

1
2
3
4
# 示例:构建 tab1 页面
cd vue-tab1
npm run build
# 生成 dist/index.html,拷贝到 flutter/assets/tab1/index.html

重复这个过程,把三个页面的构建结果(index.html + 静态资源)分别放进:

  • assets/tab1/
  • assets/tab2/
  • assets/tab3/

注意:Vue3 的构建结果路径需相对引用(建议使用相对路径打包 vite.config.js 里设置 base: './'


📦 第二步:Flutter 添加依赖

pubspec.yaml 中添加:

1
2
3
4
5
6
7
8
9
10
flutter:
assets:
- assets/tab1/
- assets/tab2/
- assets/tab3/

dependencies:
flutter:
sdk: flutter
webview_flutter: ^4.0.7 # 最新版本查 pub.dev

🧭 第三步:Flutter 主界面代码(main.dart)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:io';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter + Vue3 Web Tabs',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebTabPage(),
);
}
}

class WebTabPage extends StatefulWidget {
@override
_WebTabPageState createState() => _WebTabPageState();
}

class _WebTabPageState extends State<WebTabPage> with WidgetsBindingObserver {
int _selectedIndex = 0;
final List<String> _tabHtmlPaths = [
'assets/tab1/index.html',
'assets/tab2/index.html',
'assets/tab3/index.html',
];

@override
void initState() {
super.initState();
// 初始化 WebView 平台
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
}

String get htmlFileUrl {
return Uri.dataFromString(
File(_tabHtmlPaths[_selectedIndex]).readAsStringSync(),
mimeType: 'text/html',
encoding: Encoding.getByName('utf-8'),
).toString();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter + Vue Tabs'),
),
body: WebView(
initialUrl: htmlFileUrl,
javascriptMode: JavascriptMode.unrestricted,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) => setState(() => _selectedIndex = index),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Tab1'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Tab2'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Tab3'),
],
),
);
}
}

🔧 注意:File().readAsStringSync() 读取 asset 文件在 Android/iOS 上会报错,因为 Flutter 的 asset 不是磁盘路径。可以用 flutter_inappwebview 或将 HTML 复制为 data: URL 或使用 WebViewAssetLoader(推荐方式见下)


✅ 替代推荐:加载 Asset HTML 更安全(推荐)

使用 webview_flutter + WebViewAssetLoader 加载 Flutter 项目中的 assets:

参考: https://github.com/flutter/plugins/blob/main/packages/webview_flutter/webview_flutter/example/lib/main.dart


🔚 总结

现在拥有了:

  • ✅ Flutter 外壳(可打包 Android/iOS)
  • ✅ 三个 Tab 页面,每个加载 Vue3 构建后的 HTML 界面
  • ✅ 可扩展的结构,适合嵌入 Web 小应用或微前端子系统
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏